package vip.wangwenhao.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vip.wangwenhao.enums.ColumnTypeEnum;
import vip.wangwenhao.mojo.GeneralMojo;
import vip.wangwenhao.vo.ColumnInfo;
import vip.wangwenhao.vo.IndexInfo;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wangwenhao
 */
public class DatabaseUtil {
    private final static Logger LOGGER = LoggerFactory.getLogger(DatabaseUtil.class);

    private static final String DRIVER = "com.mysql.jdbc.Driver";
    private static String URL;
    private static String USERNAME;
    private static String PASSWORD;

    private static final String SQL = "SELECT * FROM ";
    private static final String COLUMNS_SQL = "show full columns from ";
    private static final String INDEX_SQL = "show index from ";


    public static void init(GeneralMojo generalMojo) {
        URL = generalMojo.getUrl();
        USERNAME = generalMojo.getUsername();
        PASSWORD = generalMojo.getPassword();
    }

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            LOGGER.error("can not load jdbc driver", e);
        }
    }

    /**
     * 获取数据库连接
     *
     * @return
     */
    public static Connection getConnection() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        } catch (SQLException e) {
            LOGGER.error("get connection failure", e);
        }
        return conn;
    }

    /**
     * 关闭数据库连接
     *
     * @param conn
     */
    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("close connection failure", e);
            }
        }
    }

    /**
     * 获取数据库下的所有表名
     */
    public static List<String> getTableNames() {
        List<String> tableNames = new ArrayList<>();
        Connection conn = getConnection();
        ResultSet rs = null;
        try {
            //获取数据库的元数据
            DatabaseMetaData db = conn.getMetaData();
            //从元数据中获取到所有的表名
            rs = db.getTables(null, null, null, new String[]{"TABLE"});
            while (rs.next()) {
                tableNames.add(rs.getString(3));
            }
        } catch (SQLException e) {
            LOGGER.error("getTableNames failure", e);
        } finally {
            try {
                rs.close();
                closeConnection(conn);
            } catch (SQLException e) {
                LOGGER.error("close ResultSet failure", e);
            }
        }
        return tableNames;
    }

    /**
     * 获取表的主键
     */
    public static List<ColumnInfo> getTablePrimary(String tableName) {
        List<ColumnInfo> allColumnInfos = getColumnInfos(tableName);
        List<ColumnInfo> columnInfos = new ArrayList<>();
        Connection conn = getConnection();
        ResultSet rs = null;
        try {
            //获取数据库的元数据
            DatabaseMetaData db = conn.getMetaData();

            rs = db.getPrimaryKeys(null, "%", tableName);
            while (rs.next()){
                String columnName = rs.getString("COLUMN_NAME");
                ColumnInfo primaryColumn = allColumnInfos.stream().filter(columnInfo1 -> columnInfo1.getColumnName().equals(columnName)).findFirst().get();
                columnInfos.add(primaryColumn);
            }

        } catch (SQLException e) {
            e.printStackTrace();
            LOGGER.error("getTablePrimary failure", e);
        } finally {
            try {
                if (null != rs){
                    rs.close();
                }
                closeConnection(conn);
            } catch (SQLException e) {
                LOGGER.error("close ResultSet failure", e);
            }
        }
        return columnInfos;
    }


    /**
     * 获取表中所有字段名称
     *
     * @param tableName 表名
     * @return
     */
    public static List<String> getColumnNames(String tableName) {
        List<String> columnNames = new ArrayList<>();
        //与数据库的连接
        Connection conn = getConnection();
        PreparedStatement preparedStatement = null;
        String tableSql = SQL + tableName;
        try {
            preparedStatement = conn.prepareStatement(tableSql);
            //结果集元数据
            ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData();
            //表列数
            int size = resultSetMetaData.getColumnCount();
            for (int i = 0; i < size; i++) {
                columnNames.add(resultSetMetaData.getColumnName(i + 1));
            }
        } catch (SQLException e) {
            LOGGER.error("getColumnNames failure", e);
        } finally {
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                    closeConnection(conn);
                } catch (SQLException e) {
                    LOGGER.error("getColumnNames close preparedStatement and connection failure", e);
                }
            }
        }
        return columnNames;
    }

    /**
     * 获取表中所有字段类型
     *
     * @param tableName
     * @return
     */
    public static List<String> getColumnTypes(String tableName) {
        List<String> columnTypes = new ArrayList<>();
        //与数据库的连接
        Connection conn = getConnection();
        PreparedStatement preparedStatement = null;
        String tableSql = SQL + tableName;
        try {
            preparedStatement = conn.prepareStatement(tableSql);
            //结果集元数据
            ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData();
            //表列数
            int size = resultSetMetaData.getColumnCount();
            for (int i = 0; i < size; i++) {
                columnTypes.add(resultSetMetaData.getColumnTypeName(i + 1));
            }
        } catch (SQLException e) {
            LOGGER.error("getColumnTypes failure", e);
        } finally {
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                    closeConnection(conn);
                } catch (SQLException e) {
                    LOGGER.error("getColumnTypes close preparedStatement and connection failure", e);
                }
            }
        }
        return columnTypes;
    }

    /**
     * 获取表中字段的所有注释
     *
     * @param tableName
     * @return
     */
    public static List<String> getColumnComments(String tableName) {
        //与数据库的连接
        Connection conn = getConnection();
        PreparedStatement preparedStatement = null;
        String tableSql = SQL + tableName;
        List<String> columnComments = new ArrayList<>();
        ResultSet rs = null;
        try {
            preparedStatement = conn.prepareStatement(tableSql);
            rs = preparedStatement.executeQuery(COLUMNS_SQL + tableName);
            while (rs.next()) {
                columnComments.add(rs.getString("Comment"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                    closeConnection(conn);
                } catch (SQLException e) {
                    LOGGER.error("getColumnComments close ResultSet and connection failure", e);
                }
            }
        }
        return columnComments;
    }

    /**
     * 获取表中字段的所有注释
     *
     * @param tableName
     * @return
     */
    public static List<ColumnInfo> getColumnInfos(String tableName) {
        List<ColumnInfo> columnInfos = new ArrayList<>();
        List<String> columnComments = new ArrayList<>();
        //与数据库的连接
        Connection conn = getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet rs = null;
        String tableSql = SQL + tableName;
        try {
            preparedStatement = conn.prepareStatement(tableSql);
            rs = preparedStatement.executeQuery(COLUMNS_SQL + tableName);
            while (rs.next()) {
                columnComments.add(rs.getString("Comment"));
            }
            //结果集元数据
            ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData();
            //表列数
            int size = resultSetMetaData.getColumnCount();
            for (int i = 0; i < size; i++) {
                ColumnInfo columnInfo = new ColumnInfo();
                columnInfo.setColumnName(resultSetMetaData.getColumnName(i + 1));
                columnInfo.setColumnType(ColumnTypeEnum.convert(resultSetMetaData.getColumnType(i + 1)));
                columnInfo.setColumnComment(columnComments.get(i));
                columnInfos.add(columnInfo);
            }
        } catch (SQLException e) {
            LOGGER.error("getColumnInfos failure", e);
        } finally {
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    LOGGER.error("getColumnInfos close preparedStatement and connection failure", e);
                }
            }
            if (rs != null) {
                try {
                    rs.close();
                    closeConnection(conn);
                } catch (SQLException e) {
                    LOGGER.error("getColumnInfos close ResultSet and connection failure", e);
                }
            }
        }
        return columnInfos;
    }

    /**
     * 获取表中字段的所有注释
     *
     * @param tableName
     * @return
     */
    public static List<IndexInfo> getIndex(String tableName) {
        List<IndexInfo> indexInfos = new ArrayList<>();
        Connection conn = getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet rs = null;
        String tableSql = SQL + tableName;
        try {
            preparedStatement = conn.prepareStatement(tableSql);
            rs = preparedStatement.executeQuery(INDEX_SQL + tableName);
            while (rs.next()) {
                String columnName = rs.getString("Column_name");
                String keyName = rs.getString("Key_name");
                boolean isUnique = !rs.getBoolean("Non_unique");
                boolean b = indexInfos.stream().anyMatch(indexInfo -> indexInfo.getIndexName().equals(keyName));
                if (b){
                    indexInfos.stream().filter(indexInfo -> indexInfo.getIndexName().equals(keyName)).findFirst().get().getColumnNames().add(columnName);
                }else {
                    List<String> columnNames = new ArrayList<>();
                    columnNames.add(columnName);
                    IndexInfo indexInfo = new IndexInfo();
                    indexInfo.setIndexName(keyName);
                    indexInfo.setUnique(isUnique);
                    indexInfo.setColumnNames(columnNames);
                    indexInfos.add(indexInfo);
                }
            }
            //结果集元数据
            ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData();
            //表列数
            int size = resultSetMetaData.getColumnCount();
                indexInfos.forEach(indexInfo->{
                    List<ColumnInfo> columnInfos = new ArrayList<>();
                    for (int i = 0; i < size; i++) {
                        try {
                            String columnName = resultSetMetaData.getColumnName(i + 1);
                            if (indexInfo.getColumnNames().contains(columnName)){
                                ColumnInfo columnInfo = new ColumnInfo();
                                columnInfo.setColumnName(columnName);
                                columnInfo.setColumnType(ColumnTypeEnum.convert(resultSetMetaData.getColumnType(i + 1)));
                                columnInfos.add(columnInfo);
                            }
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                    indexInfo.setColumnInfos(columnInfos);
                });

        } catch (SQLException e) {
            LOGGER.error("getIndex failure", e);
        } finally {
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    LOGGER.error("getIndex close preparedStatement and connection failure", e);
                }
            }
            if (rs != null) {
                try {
                    rs.close();
                    closeConnection(conn);
                } catch (SQLException e) {
                    LOGGER.error("getIndex close ResultSet and connection failure", e);
                }
            }
        }
        return indexInfos;
    }

    public static void main(String[] args) {
        URL = "jdbc:mysql://127.0.0.1:3306/panda?characterEncoding=utf8";
        USERNAME = "root";
        PASSWORD = "root";
        List<IndexInfo> indexInfos = getIndex("p_user");
        System.out.println(indexInfos);
    }

}